package net.stevechaloner.idea.ffs;

import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFileSystem;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.xml.DomFileDescription;
import com.intellij.util.xml.DomFileElement;
import com.intellij.util.xml.DomManager;
import net.stevechaloner.idea.ffs.actions.SearchActionFactory;
import net.stevechaloner.idea.ffs.actions.SearchGroupActionGroup;
import net.stevechaloner.idea.ffs.actions.SearchAction;
import net.stevechaloner.idea.ffs.config.Config;
import net.stevechaloner.idea.ffs.config.ConfigComponent;
import net.stevechaloner.idea.ffs.config.ConfigListener;
import net.stevechaloner.idea.ffs.model.src.Search;
import net.stevechaloner.idea.ffs.model.xml.SearchPlugin;
import net.stevechaloner.idea.ffs.io.FileFactory;
import net.stevechaloner.idea.ffs.io.GenericFileFactory;
import net.stevechaloner.idea.util.paths.Path;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;

/**
 * @author Steve Chaloner
 */
public class FirefoxSearch implements ProjectComponent,
                                      ConfigListener,
                                      SearchProviderProvider
{
    /**
     * The project.
     */
    private Project project;

    /**
     * Initialises a new instance of this class.
     * 
     * @param project the project
     */
    public FirefoxSearch(Project project)
    {
        this.project = project;
    }

    /**
     *
     */
    public void initComponent()
    {
        ConfigComponent configComponent = project.getComponent(ConfigComponent.class);
        configComponent.addConfigListener(this);
        
        init();
    }

    /**
     *
     */
    public void init()
    {
        Config config = project.getComponent(ConfigComponent.class).getConfig();

        String searchPluginsDir = config.getSearchPluginsDirectory();
        if (searchPluginsDir != null && !StringUtil.isEmptyOrSpaces(searchPluginsDir))
        {
            File f = new File(searchPluginsDir);
            if (f.exists())
            {
                VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem("file");
                VirtualFile pluginsDir = fileSystem.findFileByPath(Path.normalisePath(f));
                if (pluginsDir != null)
                {
                    VirtualFile[] files = pluginsDir.getChildren();
                    FileFactory<XmlFile> fileFactory = new GenericFileFactory<XmlFile>();
                    try
                    {
                        initSearchFiles(fileFactory.loadFiles(project,
                                                              files,
                                                              FileType.SRC));
                        initSearchPluginFiles(fileFactory.loadFiles(project,
                                                                    files,
                                                                    FileType.XML));
                    }
                    catch (IOException e)
                    {
                        clearPlugins();
                        // this should be logged, not re-thrown
                        throw new RuntimeException(e);
                    }
                }
            }
            else
            {
                clearPlugins();
            }
        }
        else
        {
            clearPlugins();
        }

    }

    /**
     *
     */
    private void clearPlugins()
    {
        SearchGroupActionGroup group = project.getComponent(SearchGroupActionGroup.class);
        if (group != null)
        {
            group.removeAllActions();
        }
    }

    /**
     *
     * @param files the search definitions
     */
    private void initSearchPluginFiles(@NotNull List<XmlFile> files)
    {
        DomManager manager = DomManager.getDomManager(project);
        manager.registerFileDescription(new DomFileDescription<SearchPlugin>(SearchPlugin.class,
                                                                             "search-plugin")
        {
            protected void initializeFileDescription()
            {
            }
        });
        SearchGroupActionGroup group = getActionGroup();
        for (XmlFile file : files)
        {
            DomFileElement<SearchPlugin> element = manager.getFileElement(file,
                                                                          SearchPlugin.class);
            if (element != null)
            {
                group.addChild(SearchActionFactory.createAction(project,
                                                                element.getRootElement()));
            }
        }
    }

    /**
     *
     * @param files the search definitions
     */
    private void initSearchFiles(@NotNull List<XmlFile> files)
    {
        DomManager manager = DomManager.getDomManager(project);
        manager.registerFileDescription(new DomFileDescription<Search>(Search.class,
                                                                       "search")
        {
            protected void initializeFileDescription()
            {
            }
        });
        SearchGroupActionGroup group = getActionGroup();
        for (XmlFile file : files)
        {
            DomFileElement<Search> element = manager.getFileElement(file,
                                                                    Search.class);
            if (element != null)
            {
                group.addChild(SearchActionFactory.createAction(project,
                                                                element.getRootElement()));
            }
        }
    }

    // javadoc unnecessary
    @NotNull
    private SearchGroupActionGroup getActionGroup()
    {
        return (SearchGroupActionGroup) ActionManager.getInstance().getAction("ffs.SearchGroupActionGroup");
    }

    // javadoc inherited
    public void disposeComponent()
    {
        getActionGroup().removeAllActions();
    }

    // javadoc inherited
    @NotNull
    public String getComponentName()
    {
        return "FirefoxSearch";
    }

    // javadoc inherited
    public void projectOpened()
    {
        // called when project is opened
    }

    // javadoc inherited
    public void projectClosed()
    {
        // called when project is being closed
    }

    // javadoc inherited
    public void configUpdated(SearchProviderProvider spp,
                              Config config)
    {
        getActionGroup().removeAllActions();
        init();
    }

    // javadoc inherited
    @NotNull
    public List<String> getSearchProviderNames()
    {
        AnAction[] actions = getActionGroup().getChildren(null);
        List<String> names = new ArrayList<String>();
        for (AnAction action : actions)
        {
            names.add(((SearchAction)action).getName());
        }
        return names;
    }
}
